package ca.tatham.senssim.diffusion;

import java.util.Iterator;

import ca.tatham.senssim.SensorType;
import ca.tatham.senssim.diffusion.GradientCache.GradientList;
import ca.tatham.senssim.diffusion.InterestMessage.Interest;
import ca.tatham.senssim.util.Address;

public class SimpleDiffusionNode extends AbstractDiffusionNode
{
  private final GradientCache m_gradientCache;
  private InterestMessage m_lastInterestMessage;

  protected SimpleDiffusionNode(Address address, int power)
  {
    super(address, power);
    m_gradientCache = new GradientCache(address);
  }

  @Override
  public boolean isSource()
  {
    return false;
  }

  @Override
  public boolean isSink()
  {
    return false;
  }

  protected GradientList getGradients(SensorType type)
  {
    return m_gradientCache.get(type);
  }

  protected Gradient getFastestGradient(SensorType type)
  {
    return m_gradientCache.get(type).getFastestGradient();
  }

  protected void removeInterest(SensorType type)
  {
    m_gradientCache.remove(type);
  }

  @Override
  protected void processInterestMessage(InterestMessage packet)
  {
    Address source = packet.getSource();
    // make sure its not from me
    if (!source.equals(getAddress()))
    {
      Interest interest = packet.getInterest();
      m_gradientCache.addOrUpdateGradient(source, interest, packet
          .getStartTime());
      if (packet.getDestination().isBroadcast())
      {
        if (m_lastInterestMessage == null
            || !m_lastInterestMessage.getInterest()
                .equals(packet.getInterest()))
        {
          sendPacket(new InterestMessage(packet, getAddress(), packet
              .getDestination()));
        }
        m_lastInterestMessage = packet;
      }
      else
      {
        // it is a reinforcement
        // log(
        // "Path from " + source + " to " + getAddress() + " reinforced.");
        // forward the interest on to ONE neighbor
        for (DataMessage data : getDataCache())
        {
          if (data.getType().equals(interest.type))
          {
            sendPacket(new InterestMessage(packet, getAddress(), data
                .getSource()));
            log("reinforcing path from " + data.getSource());
            return;
          }
        }
        log("Could not propogate reinforcement");
      }
    }
  }

  @Override
  protected void processDataMessage(DataMessage original)
  {
    if (saveData(original))
    {
      GradientList interest = getGradients(original.getType());
      if (interest != null)
      {
        for (Iterator<Gradient> it = interest.iterator(); it.hasNext();)
        {
          Gradient gradient = it.next();
          if (gradient.shouldSend())
          {
            DataMessage packet = new DataMessage(original, getAddress(),
                gradient.getAddress());
            log("Forwarding " + packet);
            sendPacket(packet);
          }

          if (gradient.isExpired())
          {
            it.remove();
          }
        }
        if (interest.isEmpty())
        {
          removeInterest(original.getType());
        }
      }
    }
    else
    {
      log("not processing data message: " + original);
    }
  }

  public void tick()
  {
    m_gradientCache.cleanup();
  }

  public char getOpenSymbol()
  {
    return '[';
  }

  public char getCloseSymbol()
  {
    return ']';
  }

}
